package com.example.rpc.demo.netty;

import com.example.rpc.demo.rpc.RpcRequest;
import com.example.rpc.demo.rpc.RpcResponse;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cglib.reflect.FastClass;
import org.springframework.cglib.reflect.FastMethod;

import java.lang.reflect.InvocationTargetException;
import java.util.Map;

public class RpcInvokeHandler extends SimpleChannelInboundHandler<RpcRequest> {

    private static final Logger logger = LoggerFactory.getLogger(RpcInvokeHandler.class);

    private final Map<String,Object> handlerMap;

    public RpcInvokeHandler(Map<String, Object> handlerMap) {
        this.handlerMap = handlerMap;
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, RpcRequest msg) throws Exception {
        RpcResponse rpcResponse = new RpcResponse();
        rpcResponse.setRequestId(msg.getRequestId());
        try {
            Object result = handle(msg);
            rpcResponse.setResult(result);
        } catch(Throwable e) {
            logger.error("invoke error: ",e);
            rpcResponse.setError(e);
            rpcResponse.setError(true);
        }
        ctx.writeAndFlush(rpcResponse).addListener(ChannelFutureListener.CLOSE);
    }

    private Object handle(RpcRequest request) throws InvocationTargetException {
        String serviceName = request.getServiceName();
        String methodName = request.getMethodName();
        Class<?>[] parameterTypes = request.getParamTypes();
        Object[] params = request.getParams();

        Object serviceBean = handlerMap.get(serviceName);
        Class<?> serviceClass = serviceBean.getClass();
        FastClass serviceFastClass = FastClass.create(serviceClass);
        FastMethod servicefastMethod = serviceFastClass.getMethod(methodName, parameterTypes);
        return servicefastMethod.invoke(serviceBean, params);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.error("server error.", cause);
        ctx.close();
    }
}
